﻿using System;
using System.Globalization;
using System.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Data.Linq.SqlClient;
using System.Text;
using System.Web;
using System.IO;
using System.Net.Mail;
using IntraVision.Web;
using IntraVision.Web.Filter;
using IntraVision.Web.Security;
using Model;
using LZMA;

public struct TaskPeriod
{
    private DateTime _sdate, _edate;
    private bool _isFree;

    public TaskPeriod(DateTime date, double period, bool isFree)
    {
        _sdate = date;
        _edate = date.AddMinutes(period);
        _isFree = isFree;
    }

    public TimeSpan StartTime
    {
        get { return _sdate.TimeOfDay; }
    }

    public TimeSpan EndTime
    {
        get { return _edate.TimeOfDay; }
    }

    public string StartTimeStr
    {
        get { return _sdate.ToTimeString(); }
    }

    public string EndTimeStr
    {
        get { return _edate.ToTimeString(); }
    }

    public string Period
    {
        get { return String.Format("{0} - {1}", StartTimeStr, EndTimeStr); }
    }

    public bool IsFree
    {
        get { return _isFree; }
    }
}

[Security]
public class TaskController : TableController<NotarialServiceDataContext, Task>
{
    protected int _ScheduleId
    {
        get { return Session["ScheduleId"] != null ? Convert.ToInt32(Session["ScheduleId"]) : 0; }
        set { Session["ScheduleId"] = value; }
    }

    protected int _PersonId = 0;

    bool create = false;

    public TaskController()
    {
        _ExcludeStateKeys.Add("tb_personid");
        if (Session["user"] is Person)
            _ConfiguratorPath = string.Format(CultureInfo.InvariantCulture, "~/xml/{0}.xml", "persontask");
    }

    protected override void EntityLoaded(NotarialServiceDataContext dc, Task entity)
    {
        ViewData["taskmode"] = Session["taskmode"];

        ViewData["notarialactions"] = (from na in dc.NotarialActions where na.IsAllowForTask && !na.IsArchive select na).ToList();
        var schedules = (from st in dc.Schedules select st).ToList();
        ViewData["schedules"] = schedules;

        if (!string.IsNullOrEmpty(Request["popup"]))
        {
            LayoutPath = "~/views/layouts/popup.haml";
            TemplatePath = "~/views/task/form.haml";

            ViewData["popup"] = true;
        }

        if (Session["user"] is Person)
        {
            TemplatePath = "~/views/task/clientform.haml";
            ViewData["frontscheduleid"] = schedules.FirstOrDefault(s => s.Name.ToLower().IndexOf("интернет") > -1).Id;

            if (!string.IsNullOrEmpty(Request["print"]))
            {
                LayoutPath = "~/views/layouts/printpage.haml";
                TemplatePath = "~/views/task/printform.haml";
            }
        }

        //Добавляем скрипт для поддержки выпадающих списков с множественным выбором и автокомплита
        RegisterScript("jquery.multipleselect.js", "table.js", "jquery.autocomplete.js");
        RegisterStylesheet("autocomplete/autocomplete.css", "task/task.css");

        bool create = entity.Id == 0;
        if (create)
        {
            entity.InitializeFromRequest(Request, null, null);
            var schedule = schedules.SingleOrDefault(s => s.Id == entity.ScheduleId);

            if (schedule != null && entity.StartDate != DateTime.MinValue)
            {
                var periods = GetPeriods(entity.StartDate, entity.ScheduleId);
                TimeSpan curTime = TimeSpan.MinValue;
                if (periods != null && periods.Count > 0)
                    foreach (TaskPeriod tp in periods) //перебираем список периодов в поисках подходящего
                        if (tp.IsFree && (entity.StartDate.TimeOfDay >= tp.StartTime || (curTime == TimeSpan.MinValue)))
                        {
                            curTime = tp.StartTime;
                            if (entity.StartDate.TimeOfDay < tp.EndTime) //попали в нужный период
                                break;
                        }
                if (curTime != TimeSpan.MinValue) //если нашли подходящее время
                {
                    entity.StartDate = entity.StartDate.AddMinutes(curTime.TotalMinutes - entity.StartDate.TimeOfDay.TotalMinutes);
                    entity.EndDate = entity.StartDate.AddMinutes(schedule.Interval);
                }
                else
                    entity.EndDate = entity.StartDate = DateTime.MinValue;
            }
        }

        ViewData["person"] = Session["user"];
    }

    protected override IQueryable ConvertQueryToResult(NotarialServiceDataContext dc, IQueryable<Task> query)
    {
        return (from q in query
                select new
                {
                    Id = q.Id,
                    Num = q.Id,
                    Comment = q.Comment,
                    NotarialActionName = q.NotarialAction.Name,
                    PersonFIO = q.Person.FIO,
                    StartDate = q.StartDate,
                    EndDate = q.EndDate,
                    Schedule = q.Schedule.Name
                });
    }

    protected override void CreateFilterConditions(NotarialServiceDataContext dc)
    {
        Filter = new Filter<Task>();
        Filter.Conditions.Add(new NumericFilterCondition<Task>("Id", "№"));
        Filter.Conditions.Add(new SelectFilterCondition<Task>("NotarialActionId", "Нотариальное действие", (from na in dc.NotarialActions orderby na.Name select na)));
        Filter.Conditions.Add(new DateFilterCondition<Task>("StartDate", "Дата начала приема"));
        Filter.Conditions.Add(new DateFilterCondition<Task>("EndDate", "Дата конца приема"));
        Filter.Conditions.Add(new UserSelectFilterCondition<Task>("PersonId", "Заявитель", (from p in dc.Persons orderby p.Name select new { Id = p.Id, Name = p.FIO }), -1, "task.ivp/findpersons", "person.ivp/select?reset=true"));
        Filter.Conditions.Add(new SelectFilterCondition<Task>("ScheduleId", "Календарь", (from na in dc.Schedules orderby na.Name select na)));
        Filter.Conditions.Add(new ManualFilterCondition<Task>("manual", "Дополнительно"));
    }

    protected override IQueryable<Task> CustomFilter(NotarialServiceDataContext dc, IQueryable<Task> query)
    {
        var list = query;
        var user = Session["user"] as IUser;

        if (_ScheduleId > 0)
        {
            list = list.Where(t => t.ScheduleId == _ScheduleId);
        }

        if (user is Person)
        {
            return list.Where(q => q.PersonId == user.Id);
        }

        if (!string.IsNullOrEmpty(State["tb_personid"]))
            Int32.TryParse(State["tb_personid"], out _PersonId);
        return (_PersonId == 0) ? list.Where(a => true) : list.Where(a => a.PersonId == _PersonId);
    }

    protected override void CustomInit(NotarialServiceDataContext dc)
    {
        if (ViewData["user"] is User)
        {
            var user = ViewData["user"] as IUser;

            if (!string.IsNullOrEmpty(Request["ScheduleId"])) _ScheduleId = Convert.ToInt32(Request["ScheduleId"]);

            ViewData["ScheduleId"] = _ScheduleId;

            if (_ScheduleId > 0)
            {
                var schedule = dc.Schedules.SingleOrDefault(s => s.Id == _ScheduleId);
                if (schedule != null)
                    ViewData["schedule"] = schedule;
                else
                    _ScheduleId = 1;
            }
        }
    }

    protected override IQueryable<Task> Search(IQueryable<Task> query)
    {
        int tid;
        //Добавим поиск по идентификатору заявки
        if (!string.IsNullOrEmpty(State["tb_search"]))
        {
            //поиск по заявителям
            int[] persons;
            using (var dc = new NotarialServiceDataContext())
            {
                //string search = string.Format("%{0}%", State["tb_search"].Replace(' ', '%'));
                string search = State["tb_search"].Trim().ToLower();
                persons = (from p in dc.Persons.ToList<Person>().Where<Person>(p => p.FIO.ToLower().IndexOf(search) > -1) select p.Id).ToArray<int>();
                //clients = (from c in dc.Clients where (SqlMethods.Like(c.Name, search) || SqlMethods.Like(c.LastName, search) || SqlMethods.Like(c.PatronymicName, search) ) select c.Id).ToArray<int>();
            }
            query = (base.Search(query).Union<Task>(query.Where(t => ((Int32.TryParse(State["tb_search"], out tid)) && t.Id == tid) || ((persons.Length > 0) && persons.Contains(t.PersonId))))).Distinct<Task>();
        }
        return query;
    }

    public override void View(string entityid)
    {
        var user = ViewData["user"] as IUser;

        if (!String.IsNullOrEmpty(Request["readonly"]) || !user.HasPermission("TaskAdmin"))
            ViewData["readonly"] = true;

        if (!(user).HasPermission("IsAdmin"))
        {
            int taskid;
            Int32.TryParse(entityid, out taskid);
            using (var dc = new NotarialServiceDataContext())
            {
                var task = dc.Tasks.SingleOrDefault(t => t.Id == taskid && t.PersonId == user.Id);
                if (task == null)
                    return;
            }
        }

        base.View(entityid);
    }

    public override void List()
    {
        RegisterScript("task/list.js");

        //Запишем в сессию, что мы работали со списком
        Session["taskmode"] = "task.ivp/list";

        if (ViewData["user"] is User)
            ViewData["schedules"] = (from st in (new NotarialServiceDataContext()).Schedules select st).ToList<Schedule>();

        if (Session["activationmessage"] != null)
        {
            ViewData["message"] = Session["activationmessage"];
            Session["activationmessage"] = null;
        }

        base.List();
    }

    [AllowedRole("TaskAdmin")]
    public override void Create()
    {
        base.Create();
    }

    [AllowedRole("TaskAdmin")]
    public override void Delete()
    {
        base.Delete();
    }

    protected override void BeforeSubmit(NotarialServiceDataContext dc, Task entity)
    {
        create = (entity.Id == 0);

        if (create && !string.IsNullOrEmpty(Request["personid"]))
        {
            int pid = Convert.ToInt32(Request["personid"]);
            if (pid > 0)
            {
                ViewData["taskperson"] = (from p in dc.Persons where p.Id == pid select p).FirstOrDefault();
            }
        }
        base.BeforeSubmit(dc, entity);
    }

    protected override void AfterSubmit(NotarialServiceDataContext dc, Task entity)
    {
        if (Session["user"] is User)
            Logger.WriteLog(dc, (create) ? LogOperationType.TaskCreate : LogOperationType.TaskChange, (Session["User"] as User).Id, null, null);

        if (create && (Session["user"] is Person)) //при создании заявки во фронт-зоне клиентом
        {
            //уведомление пользоваетлю по заявке
            var person = entity.Person;
            if (!string.IsNullOrEmpty(person.Email))
            {
                var host = ConfigurationManager.AppSettings["smtphost"] ?? "localhost";
                var from = ConfigurationManager.AppSettings["mailfrom"];
                var body = ConfigurationManager.AppSettings["clienttaskmessage"] ?? "Уважаемый(-ая) %clientfio%. \r\nВы оставили заявку на прием в нотариальной конторе. Номер заявки: %taskid%.";

                var path = Request.PhysicalApplicationPath + body;
                if (File.Exists(path))
                    body = File.ReadAllText(path, Encoding.Default);

                int port;
                Int32.TryParse(ConfigurationManager.AppSettings["smtpport"], out port);
                if (port <= 0) port = 25;

                try
                {
                    var smtp = new SmtpClient(host, port);
                    var mm = new MailMessage(from, person.Email);

                    mm.Subject = "Запись на прием.";
                    mm.Body = body.Replace("%clientfio%", person.FIO)
                    .Replace("%taskid%", entity.Id.ToString())
                    .Replace("%period%", entity.StartDate.ToString())
                    .Replace("%notarialaction%", entity.NotarialAction.Name)
                    .Replace("%comment%", entity.Comment);

                    mm.IsBodyHtml = false;

                    smtp.Send(mm);
                }
                catch { }
            }
            create = false;
        }

        string printlink = String.Format("<a href=\"#\" onclick=\"print({0}); return false;\">Распечатайте форму</a>", entity.Id);
        string message = String.Format("Спасибо, Ваша заявка принята под номером {0}.<br />{1} и представьте ее в нотариальную контору, либо запишите номер и назовите его в нотариальной конторе.", entity.Id, printlink);

        ViewData["message"] = message;
    }

    protected override void EntityDeleted(NotarialServiceDataContext dc, List<Task> deleted)
    {
        if (Session["User"] is User)
            foreach (var entity in deleted)
                Logger.WriteLog(dc, LogOperationType.TaskDelete, (Session["User"] as User).Id, null, null);
    }

    public void FindPersons()
    {
        if (string.IsNullOrEmpty(Request["q"])) return;
        string search = string.Format("%{0}%", Request["q"].Replace(' ', '%'));

        using (var dc = new NotarialServiceDataContext())
        {
            StringBuilder sb = new StringBuilder();
            var persons = (from p in dc.Persons
                           where !p.IsArchive && (SqlMethods.Like(p.Name, search) || SqlMethods.Like(p.LastName, search) || SqlMethods.Like(p.PatronymicName, search))
                           orderby p.LastName
                           select new { p.Id, p.FIO });
            foreach (var person in persons)
            {
                sb.AppendLine(String.Format("{0}|{1}", person.FIO, person.Id));
            }
            Response.Write(sb.ToString());
        }
    }

    public void FindCompanies()
    {
        if (string.IsNullOrEmpty(Request["q"])) return;
        string search = string.Format("%{0}%", Request["q"].Replace(' ', '%'));

        using (var dc = new NotarialServiceDataContext())
        {
            StringBuilder sb = new StringBuilder();
            var companies = (from p in dc.Companies
                             where !p.IsArchive && (SqlMethods.Like(p.Name, search))
                           orderby p.Name
                           select new { p.Id, p.Name });
            foreach (var person in companies)
            {
                sb.AppendLine(String.Format("{0}|{1}", person.Name, person.Id));
            }
            Response.Write(sb.ToString());
        }
    }
    public void FindUsers()
    {
        if (string.IsNullOrEmpty(Request["q"])) return;
        string search = string.Format("%{0}%", Request["q"].Replace(' ', '%'));

        using (var dc = new NotarialServiceDataContext())
        {
            StringBuilder sb = new StringBuilder();
            var users = (from u in dc.Users
                           where !u.IsArchive && (SqlMethods.Like(u.Name, search))
                           orderby u.Name
                           select new { u.Id, u.Name });
            foreach (var user in users)
            {
                sb.AppendLine(String.Format("{0}|{1}", user.Name, user.Id));
            }
            Response.Write(sb.ToString());
        }
    }

    [AllowedRole("IsAdmin")]
    public void LoadPersonTasks()
    {
        _ActionKey = Action;
        ViewData["hidecb"] = true;

        TemplatePath = "~/views/person/persontask.haml";
        LayoutPath = "none";
        
        ProcessTable();
    }

    public void GetPeriods()
    {
        if (Request["date"] != null && Request["scheduleid"] != null)
        {
            //получаем параметры из request'а
            DateTime date;
            int scheduleId;
            DateTime.TryParse(Request["date"], out date);
            Int32.TryParse(Request["scheduleid"], out scheduleId);

            ViewData["periods"] = GetPeriods(date, scheduleId);
            ViewData["currentDate"] = date.ToString("dd MMMM");
        }
        TemplatePath = "~/views/task/_periods.haml";
        RenderView();
    }

    public List<TaskPeriod> GetPeriods(DateTime date, int scheduleId)
    {
        if (date != DateTime.MinValue && scheduleId > 0)
        {
            using (var dc = new NotarialServiceDataContext())
            {
                var schedule = dc.Schedules.SingleOrDefault(s => s.Id == scheduleId);
                //определяем шаг периода
                int interval = schedule.Interval;
                var day = schedule.GetWorkingHours(date);
                if (day != null)
                    return SplitPeriod(schedule.Tasks.Where(t => t.StartDate.Date == date.Date).ToList<Task>(), day, date, interval);
            }
        }
        return null;
    }

    private List<TaskPeriod> SplitPeriod(List<Task> taskList, IWorkingHours day, DateTime date, double step)
    {
        var result = new List<TaskPeriod>();

        DateTime? sDate = day.TimeStart;
        DateTime? eDate = day.TimeEnd;
        DateTime? bufDate;

        bool isFree;
        bool hasBreak = (day.BreakStart.HasValue && day.BreakEnd.HasValue);

        if (sDate.HasValue && eDate.HasValue)
        {
            while (sDate <= eDate.Value.AddMinutes(-step))
            {
                if (hasBreak && sDate.Value.TimeOfDay >= day.BreakStart.Value.TimeOfDay && sDate.Value.TimeOfDay <= day.BreakEnd.Value.TimeOfDay)
                    sDate = day.BreakEnd;
                bufDate = sDate.Value.AddMinutes(step);
                if (!(hasBreak && sDate.Value.TimeOfDay < day.BreakStart.Value.TimeOfDay && bufDate.Value.TimeOfDay > day.BreakStart.Value.TimeOfDay))
                {
                    isFree = !taskList.Exists(t => (t.StartDate.TimeOfDay < sDate.Value.TimeOfDay && t.EndDate.TimeOfDay > sDate.Value.TimeOfDay) 
                                                    || (t.StartDate.TimeOfDay < bufDate.Value.TimeOfDay && bufDate.Value.TimeOfDay < t.EndDate.TimeOfDay)
                                                    || (t.StartDate.TimeOfDay >= sDate.Value.TimeOfDay && t.EndDate.TimeOfDay <= bufDate.Value.TimeOfDay));
                    result.Add(new TaskPeriod(Convert.ToDateTime(sDate), step, isFree));
                }
                sDate = bufDate;
            }
        }
        return result;
    }

    public void GetNotarialDescription(string idStr)
    {
        int id = 0;
        Int32.TryParse(idStr, out id);
        if (id > 0)
        {
            using (var dc = new NotarialServiceDataContext())
            {
                string description = "";
                var action = dc.NotarialActions.FirstOrDefault(na => na.Id == id);
                if (action != null)
                    description = action.Description;
                RenderJSON(description);
            }
        }
        return;
    }

    protected override void ReturnAfterSave(string page)
    {
        Response.Redirect(Convert.ToString(Session["taskmode"]));
    }
}
